home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
pcpilot.arc
/
TSR.C
< prev
Wrap
Text File
|
1990-01-14
|
11KB
|
402 lines
/*
TSR.C
Original code from Al Stevens' book "Extending Turbo C Professional"
slightly modified by Tom Grubbe
NOTE: In the listing in his book, Mr. Stevens #includes "interrupt.h"
which is nowhere to be found in the entire book! However with
some added #defines and the IREGS structure this module compiles
and performs without errors.
*/
#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <conio.h>
#define TRUE 1
#define FALSE 0
/* --- vectors ---- */
#define DISK 0x13
#define INT28 0x28
#define KYBRD 0x9
#define CRIT 0x24
#define DOS 0x21
#define CTRLC 0x23
#define CTRLBRK 0x1b
#define TIMER 0x1c
typedef struct {
int bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl;
} IREGS;
unsigned scancode;
unsigned keymask;
extern char signature[];
int unloading; /* TSR unload flag */
static void (*UserRtn)(void); /* Pointer to user's start routine */
static void (*InitRtn)(void); /* Pointer to user's initialization routine */
/* ---- interrupt vector chains ----- */
static void interrupt (*oldbreak)(void);
static void interrupt (*oldctrlc)(void);
static void interrupt (*oldtimer)(void);
static void interrupt (*old28)(void);
static void interrupt (*oldkb)(void);
static void interrupt (*olddisk)(void);
static void interrupt (*oldcrit)(void);
/* ------ ISRs fot the TSR --------- */
static void interrupt newtimer(void);
static void interrupt new28(void);
static void interrupt newkb(void);
static void interrupt newdisk(IREGS);
static void interrupt newcrit(IREGS);
static void interrupt newbreak(void);
static unsigned sizeprogram;/* TSR's program size */
static unsigned dosseg; /* DOS segment address */
static unsigned dosbusy; /* offset to InDos flag */
static unsigned psps[2]; /* table of DOS PSP addresses */
static int pspctr; /* # of DOS PSP addresses */
static int diskflag; /* disk BIOS busy flag */
static unsigned mcbseg; /* address of 1st DOS mcb */
static char far *mydta; /* TSR's DTA */
static unsigned myss; /* TSR's stack segment */
static unsigned mysp; /* TSR's stack pointer */
static unsigned intpsp; /* Interrupted PSP address */
static int running; /* TSR running indicator */
static int hotkey_flag; /* Hotkey pressed flag */
/* ------ local prototypes ------- */
void tsr(void (*FPtr)(void), void (*InitFPtr)(void));
static void tsr_init(void);
static void resinit(void);
static void unload(void);
static void resterm(void);
static void pspaddr(void);
static void dores(void);
static void resident_psp(void);
static void interrupted_psp(void);
static int resident(char *signature);
static int test_hotkeys(int ky);
#define signon(s) printf("\n%s %s", signature, s);
void tsr(void (*FPtr)(void), void (*InitFPtr)(void))
{
UserRtn = FPtr;
InitRtn = InitFPtr;
tsr_init();
if (resident(signature) == FALSE) {
/* ------- initial load of TSR program -------- */
#ifdef DEBUG
(*UserRtn)();
return;
#else
/* ------- Terminate and Stay Resident -------- */
(*InitRtn)(); /* user's init function */
resinit();
#endif
}
signon("is already installed.\n");
}
/* --------- initialize TSR control values ---------- */
static void tsr_init()
{
unsigned es, bx;
/* --------- get address of DOS busy flag --------- */
_AH = 0x34;
geninterrupt(DOS);
dosseg = _ES;
dosbusy = _BX;
/* --------- get the seg addr of 1st DOS MCB --------- */
_AH = 0x52;
geninterrupt(DOS);
es = _ES;
bx = _BX;
mcbseg = peek(es, bx-2);
/* --------- get address of resident program's dta -------- */
mydta = getdta();
/* --------- get address of PSP in DOS 2.x --------- */
if (_osmajor < 3)
pspaddr();
}
/* --------- establish & declare residency ---------- */
static void resinit()
{
myss = _SS;
mysp = _SP;
oldtimer = getvect(TIMER);
old28 = getvect(INT28);
oldkb = getvect(KYBRD);
olddisk = getvect(DISK);
/* ------- attach vectors to resident program ------- */
setvect(TIMER, newtimer);
setvect(KYBRD, newkb);
setvect(INT28, new28);
setvect(DISK, newdisk);
/* -------- compute program's size -------- */
sizeprogram = myss + ((mysp+50) / 16) - _psp;
/* -------- terminate and stay resident -------- */
keep(0, sizeprogram);
}
/* --------- break handler ----------- */
static void interrupt newbreak()
{
return;
}
/* ---------- critical error ISR --------- */
static void interrupt newcrit(IREGS ir)
{
ir.ax = 0; /* ignore critical errors */
}
/* -------- BIOS disk functions ISR --------- */
static void interrupt newdisk(IREGS ir)
{
diskflag++;
(*olddisk)();
ir.ax = _AX; /* for the register returns */
ir.cx = _CX;
ir.dx = _DX;
ir.fl = _FLAGS;
--diskflag;
}
/* -------- test for the hotkey --------- */
static int test_hotkeys(int ky)
{
static unsigned biosshift;
biosshift = peekb(0, 0x417);
if (ky == scancode && (biosshift & keymask) == keymask)
hotkey_flag = !running;
return hotkey_flag;
}
/* --------- keyboard ISR ---------- */
static void interrupt newkb()
{
static int kbval;
if (test_hotkeys(inportb(0x60))) {
/* reset the keyboard */
kbval = inportb(0x61);
outportb(0x61, kbval | 0x80);
outportb(0x61, kbval);
outportb(0x20, 0x20);
}
else
(*oldkb)();
}
/* --------- timer ISR ---------- */
static void interrupt newtimer()
{
(*oldtimer)();
test_hotkeys(0);
if (hotkey_flag && peekb(dosseg, dosbusy) == 0) {
if (diskflag == 0) {
outportb(0x20, 0x20);
hotkey_flag = FALSE;
dores();
}
}
}
/* ---------- 0x28 ISR ---------- */
static void interrupt new28()
{
(*old28)();
if (hotkey_flag && peekb(dosseg, dosbusy) != 0) {
hotkey_flag = FALSE;
dores();
}
}
/* ------ switch psp context from interrupted to TSR ------ */
static void resident_psp()
{
int pp;
if (_osmajor < 3) {
/* --- save interrupted program's psp (DOS 2.x) ---- */
intpsp = peek(dosseg, *psps);
/* ------- set resident program's psp ------- */
for (pp = 0; pp < pspctr; pp++)
poke(dosseg, psps[pp], _psp);
}
else {
/* ----- save interrupted program's psp ------ */
intpsp = getpsp();
/* ------ set resident program's psp ------- */
_AH = 0x50;
_BX = _psp;
geninterrupt(DOS);
}
}
/* -------- switch psp context from TSR to interrupted --------- */
static void interrupted_psp()
{
int pp;
if (_osmajor < 3) {
/* --- reset interrupted psp (DOS 2.x) ---- */
for (pp = 0; pp < pspctr; pp++)
poke(dosseg, psps[pp], intpsp);
}
else {
/* ------ reset interrupted psp ------- */
_AH = 0x50;
_BX = intpsp;
geninterrupt(DOS);
}
}
/* -------- execute the resident program ---------- */
static void dores()
{
static char far *intdta; /* interrupted DTA */
static unsigned intsp; /* " stack pointer */
static unsigned intss; /* " stack segment */
static unsigned ctrl_break; /* Ctrl-Break setting */
running = TRUE; /* set TSR running metaphore */
disable();
intsp = _SP;
intss = _SS;
_SP = mysp;
_SS = myss;
oldcrit = getvect(CRIT);
oldbreak = getvect(CTRLBRK);
oldctrlc = getvect(CTRLC);
setvect(CRIT, newcrit);
setvect(CTRLBRK, newbreak);
setvect(CTRLC, newbreak);
ctrl_break = getcbrk(); /* get ctrl break setting */
setcbrk(0); /* turn off ctrl break logic */
intdta = getdta(); /* get interrupted dta */
setdta(mydta); /* set resident dta */
resident_psp(); /* swap psps */
enable();
(*UserRtn)(); /* call the TSR program here */
disable();
interrupted_psp(); /* reset interrupted psp */
setdta(intdta); /* reset interrupted dta */
setvect(CRIT, oldcrit); /* reset critical error */
setvect(CTRLBRK, oldbreak);
setvect(CTRLC, oldctrlc);
setcbrk(ctrl_break); /* reset ctrl break */
_SP = intsp; /* reset interrupted stack */
_SS = intss;
enable();
if (unloading)
unload();
running = FALSE;
}
/* ------ test to see if the program is already resident -------- */
static int resident(char *signature)
{
char *sg;
unsigned df;
unsigned blkseg, mcbs = mcbseg;
df = _DS - _psp;
/* --- walk through mcb chain & search for TSR --- */
while (peekb(mcbs, 0) == 0x4d) {
blkseg = peek(mcbs, 1);
if (peek(blkseg, 0) == 0x20cd) {
/* ---- this is a psp ---- */
if (blkseg == _psp)
break; /* if the transient copy */
for (sg = signature; *sg; sg++)
if (*sg != peekb(blkseg+df, (unsigned)sg))
break;
if (*sg == '\0')
/* ---------- TSR is already resident ----------- */
return TRUE;
}
mcbs += peek(mcbs, 3) + 1;
}
return FALSE;
}
/* --------- find address of PSP (DOS 2.x) ----------- */
static void pspaddr()
{
unsigned adr = 0;
disable();
/* ------- search for matches on the psp in dos -------- */
while (pspctr < 2 &&
(unsigned)((dosseg<<4) + adr) < (mcbseg<<4)) {
if (peek(dosseg, adr) == _psp) {
/* ------ matches psp, set phoney psp ------- */
_AH = 0x50;
_BX = _psp + 1;
geninterrupt(DOS);
/* ------ did matched psp change to the phoney? ----- */
if (peek(dosseg, adr) == _psp + 1)
/* ------ this is a DOS 2.x psp placeholder ----- */
psps[pspctr++] = adr;
/* ----- reset the original psp ------ */
_AH = 0x50;
_BX = _psp;
geninterrupt(DOS);
}
adr++;
}
enable();
}
/* -------- unload the rsident program --------- */
static void unload()
{
if (getvect(DISK) == (void interrupt (*)()) newdisk)
if (getvect(KYBRD) == newkb)
if (getvect(INT28) == new28)
if (getvect(TIMER) == newtimer) {
resterm();
return;
}
/* --- another TSR is above us, cannot unload --- */
putch(7);
}
/* --------- TSR unload function ----------- */
static void resterm()
{
unsigned mcbs = mcbseg;
/* restore the interrupted vectors */
setvect(TIMER, oldtimer);
setvect(KYBRD, oldkb);
setvect(INT28, old28);
setvect(DISK, olddisk);
/* obliterate the signature */
*signature = '\0';
/* walk through mcb chain &
release memory owned by the TSR */
while (peekb(mcbs, 0) == 0x4d) {
if (peek(mcbs, 1) == _psp)
freemem(mcbs+1);
mcbs += peek(mcbs, 3) + 1;
}
}